/**
 * Copyright (C) 2012 The SkyTvOS Project
 * <p/>
 * Version     Date           Author
 * ─────────────────────────────────────
 * 2015年1月20日         Root.Lu
 */

package com.coocaa.x.framework.utils;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build.VERSION;
import android.os.Environment;
import android.os.RemoteException;
import android.os.StatFs;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.util.Log;

import com.coocaa.x.framework.app.CoocaaApplication;
import com.coocaa.x.framework.utils.Storage.SDevice.MEDIA_TYPE;
import com.coocaa.x.service.XServiceManager;
import com.skyworth.framework.skysdk.android.SkySystemUtil;
import com.skyworth.framework.skysdk.properties.SkyGeneralProperties;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Storage {
    private static final String TAG = "Storage";

    private static final String ROOT_FOLDER = "";
    private static String EXT_STORAGE_FOLDER = "";
    private static Context mContext = null;
    private static List<IStorageListener> listeners = new ArrayList<IStorageListener>();
    private static Cache<String, SDevice> sdevices = new Cache<String, SDevice>();

    public static class SDevice {
        public static enum MEDIA_TYPE {
            SDCARD, DISK
        }

        private String ID = null;
        private String mountPoint = null;
        private String label = null;
        private MEDIA_TYPE mediaType = null;
        private boolean bMounted = false;
        private boolean bEmulated = false;
        private boolean bExternal = false;

        @Override
        public boolean equals(Object o) {
            // TODO Auto-generated method stub
            try {
                return mountPoint.equals(((SDevice) o).getMountPoint()) && bMounted == ((SDevice) o).bMounted;
            } catch (Exception e) {
                return false;
            }
        }

        public boolean isEmulated() {
            return bEmulated;
        }

        public void setEmulated(boolean bEmulated) {
            this.bEmulated = bEmulated;
        }

        public boolean isMounted() {
            return bMounted;
        }

        public void setMounted(boolean bMounted) {
            this.bMounted = bMounted;
        }

        public String getID() {
            return ID;
        }

        public void setID(String ID) {
            this.ID = ID;
        }

        public String getMountPoint() {
            return mountPoint;
        }

        public void setMountPoint(String mountPoint) {
            this.mountPoint = mountPoint;
        }

        public String getLabel() {
            return label;
        }

        public void setLabel(String label) {
            this.label = label;
        }

        public MEDIA_TYPE getMediaType() {
            return mediaType;
        }

        public void setMediaType(MEDIA_TYPE mediaType) {
            this.mediaType = mediaType;
        }

        public boolean isExternal() {
            return bExternal;
        }

        public void setExternal(boolean bExternal) {
            this.bExternal = bExternal;
        }
    }

    public interface IStorageListener {
        void onStorageMounted(SDevice device);

        void onStorageRemoved(SDevice device);
    }

    private static final BroadcastReceiver deviceChangeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(final Context context, final Intent intent) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    String action = intent.getAction();
                    LogUtils.i(TAG, "deviceChangeReceiver:" + action);
                    if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        updateSDevices(scanSDevices());
                    } else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) {

                    } else if (action.equals(Intent.ACTION_MEDIA_EJECT)) {

                    } else if (action.equals(Intent.ACTION_MEDIA_REMOVED)) {
                        updateSDevices(scanSDevices());
                    }
                }
            };
            CoocaaApplication.post(runnable);
        }
    };

    private static boolean containsDevice(SDevice device, List<SDevice> devices) {
        for (SDevice _device : devices) {
            if (_device.equals(device))
                return true;
        }
        return false;
    }

    private static void updateSDevices(List<SDevice> list) {
        List<SDevice> _list = sdevices.values();
        boolean flag = false;
        if (_list.size() != list.size())
            flag = true;
        else {
            for (SDevice device1 : list) {
                boolean _flag = false;
                for (SDevice device2 : _list) {
                    if (device1.equals(device2)) {
                        _flag = true;
                        break;
                    }
                }
                if (!_flag) {
                    flag = true;
                    break;
                }
            }
        }
        LogUtils.i(TAG, "updateSDevices flag:" + flag);
        for (SDevice device : _list)
            LogUtils.i(TAG, "updateSDevices old:" + device.getMountPoint());
        for (SDevice device : list)
            LogUtils.i(TAG, "updateSDevices now:" + device.getMountPoint());
        if (flag) {
            for (SDevice device : list) {
                if (!containsDevice(device, _list)) {
                    sdevices.add(device.getMountPoint(), device);
                    LogUtils.i(TAG, "device mounted:" + device.getMediaType().toString() + "  mp:" + device.getMountPoint());
                    notifyDeviceMounted(device);
                }
            }
            for (SDevice device : _list) {
                if (!containsDevice(device, list)) {
                    sdevices.remove(device.getMountPoint());
                    LogUtils.i(TAG, "device unmounted:" + device.getMediaType().toString() + "  mp:" + device.getMountPoint());
                    notifyDeviceRemoved(device);
                }
            }
        }
    }

    private static void notifyDeviceRemoved(SDevice device) {
        LogUtils.i(TAG, "notifyDeviceRemoved size:" + listeners.size());
        synchronized (listeners) {
            for (IStorageListener l : listeners)
                try {
                    l.onStorageRemoved(device);
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    }

    private static void notifyDeviceMounted(SDevice device) {
        LogUtils.i(TAG, "notifyDeviceMounted size:" + listeners.size());
        synchronized (listeners) {
            for (IStorageListener l : listeners)
                try {
                    l.onStorageMounted(device);
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    }

    public static synchronized void setContext(Context context) {
        if (mContext == null) {
            mContext = context;
            EXT_STORAGE_FOLDER = "/." + mContext.getPackageName() + "/";
            CoocaaApplication.post(new Runnable() {
                @Override
                public void run() {
                    updateSDevices(scanSDevices());
                    LogUtils.i(TAG, "setContext init external devices  !!");
                }
            });

            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
            filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
            filter.addAction(Intent.ACTION_MEDIA_EJECT);
            filter.addAction(Intent.ACTION_MEDIA_REMOVED);
            filter.addDataScheme("file");
            mContext.registerReceiver(deviceChangeReceiver, filter);
        }
    }

    public static void addListener(IStorageListener listener) {
        synchronized (listeners) {
            if (!listeners.contains(listener) && listener != null)
                listeners.add(listener);
        }
    }

    public static void removeListener(IStorageListener listener) {
        synchronized (listeners) {
            if (listener != null)
                listeners.remove(listener);
        }
    }

    public static synchronized long getSDCardFreeSpace() {
        List<SDevice> devices = sdevices.values();
        for (SDevice device : devices)
            if (device.getMediaType() == MEDIA_TYPE.SDCARD)
                return getFreeSpace(device, false);
        return 0;
    }

    public static synchronized long getInternalMemoryFreeSpace() {
        path = getInternalMemoryPath(ROOT_FOLDER);
        return getFreeSpace(path, false);
    }

    public static String SHAARE_KEY = "reservedMemory";
    public static String path;

    /**
     * 获取配置的保留空间大小
     * @return 单位为B
     */
    public static long getConfigSize() {
        long configSize = (long)SkyGeneralProperties.getIntConfig("x_downloader_reserve_space");  //获取配置大小 单位为MB
        return configSize*1024*1024;
    }

    public static synchronized void ThreadSleep() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static boolean checkNewTaskCanDownLoad(String path,  long reserved, long configSize, long totalSpace) {
        long freeSize = 0;

        if(path == null || path.equals("")) {
            return false;
        }

        freeSize = getFreeSpace(path, false);

        LogUtils.i("Storage", "freeSize: "+freeSize+", totalSpace: "+totalSpace+", totalSpace/10: "+", reserved: "+reserved);
        if (configSize > 0) {
            if(freeSize > configSize + reserved) {
                return true;
            } else {
                LogUtils.i("Storage", "freeSize = "+freeSize+", configSize + reserved = "+(configSize + reserved));
                LogUtils.i("Storage", "Free Space Less Than Config Set. path: "+path);
            }
        } else {
            if(freeSize > totalSpace/10 + reserved) {
                return true;
            } else {
                LogUtils.i("Storage", "freeSize = "+freeSize+", totalSpace/10 + reserved = "+(totalSpace/10 + reserved));
                LogUtils.i("Storage", "Free Space Less Than Config Set. path: "+path);
            }
        }

        return false;
    }
    /**
     *比较剩余空间大小是否大于配置大小，如果返回false，则使用剩余空间百分之十判断
     * @param path 保存路径
     * @return
     */
    public static boolean isHasEnoughSpaceToDownload(String path,  long configSize, long totalSpace) {
        long freeSize = 0;

        if(path == null || path.equals("")) {
            return false;
        }

        if (configSize > 0) {
            freeSize = getFreeSpace(path, false);
            if(freeSize > configSize) {
                return true;
            } else {
                LogUtils.i("Storage", "freeSize = "+freeSize+", configSize = "+configSize);
                LogUtils.i("Storage", "Free Space Less Than Config Set. path: "+path);
            }
        } else {
            return !isFreeSpaceLessThanOneTenth(path, totalSpace);
        }
        return false;
    }
    /**
     * 判断指定路径下剩余空间大小是否超过总空间大小十分之一
     * @param path
     * @param totalSpace
     * @return
     */
    public static synchronized boolean isFreeSpaceLessThanOneTenth(String path, long totalSpace) {
        try {
            long freeSpace =getFreeSpace(path, false);
            if(totalSpace/freeSpace >= 10) {
                LogUtils.i("Storage", "totalSpace = "+totalSpace+", freeSpace = "+freeSpace);
                LogUtils.i("Storage", "freeSpace Less Than OneTenth. path: "+path);
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            LogUtils.i("Storage", ""+e);
            return true;
        }
    }

    public static synchronized void refreshInternalMemoryPath() {
        path = getInternalMemoryPath(ROOT_FOLDER);
    }

    public static synchronized long getInternalFreeMemory() {
        return getFreeSpace(path, false);
    }

    public static synchronized long getReservedMemory() {
        // return SharePreferenceManager.getInstance().getLongValue(SHAARE_KEY, 0);
        return 0;
    }

    public static synchronized void setReservedMemory(long reservedMemory) {
        // long mSize = SharePreferenceManager.getInstance().getLongValue(SHAARE_KEY, 0) +
        // reservedMemory;
        // if(mSize < 0)
        // {
        // mSize = 0;
        // }
        // SharePreferenceManager.getInstance().saveLongConfig(SHAARE_KEY, mSize);
    }

    public static synchronized void resetReservedMemory() {
        // SharePreferenceManager.getInstance().saveLongConfig(SHAARE_KEY, 0);
    }

    public static synchronized String getSDevicePath(SDevice device, String folder) {
        String path = device.getMountPoint();
        if (folder.equals(ROOT_FOLDER))
            return path;
        path += "/" + folder + "/";
        File file = new File(path);
        if (!file.exists())
            file.mkdirs();
        path = file.getAbsolutePath() + "/";
        file.setReadable(true, false);
        file.setWritable(true, false);
        // chmod(path, 777);
        return path;
    }

    public static synchronized String getSDCardPath(String folder) {
        if (isSDCardAvaiable()) {
            if (Environment.getExternalStorageDirectory().canWrite()) {
                String path = Environment.getExternalStorageDirectory().getPath();
                if (folder.equals(ROOT_FOLDER))
                    return path;
                path += EXT_STORAGE_FOLDER + folder;
                File file = new File(path);
                if (!file.exists())
                    file.mkdirs();
                path = file.getAbsolutePath() + "/";
                file.setReadable(true, false);
                file.setWritable(true, false);
                // chmod(path, 777);
                return path;
            }
        }
        return null;
    }

    private static boolean isSDCardAvaiable() {
        try {
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
                return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static synchronized String getInternalMemoryPath(String folder) {
        String path = mContext.getFilesDir().getAbsolutePath();
        chmod(path, 777);
        if (folder.equals(ROOT_FOLDER))
            return path;
        path += "/" + folder + "/";
        File file = new File(path);
        if (!file.exists())
            file.mkdirs();
        path = file.getAbsolutePath() + "/";
        file.setReadable(true, false);
        file.setWritable(true, false);
        // chmod(path, 777);
        return path;
    }

    public static List<SDevice> getSDeviceList() {
        if (sdevices.size() == 0) {
            List<SDevice> devices = scanSDevices();
            for (SDevice device : devices)
                sdevices.add(device.getMountPoint(), device);
        }
        return sdevices.values();
    }

    public static long getFreeSpace(SDevice device, boolean bwait) {
        return getFreeSpace(device.getMountPoint(), bwait);
    }

    public static long getTotalSpace(SDevice device, boolean bwait) {
        return getTotalSpace(device.getMountPoint(), bwait);
    }

    public static synchronized long getFreeSpace(String path, boolean bwait) {
        try {
            if (bwait)
                Thread.sleep(2000);
            StatFs sf = new StatFs(path);
            long blockSize = sf.getBlockSize();
            long availCount = sf.getAvailableBlocks();
            return blockSize * availCount;
        } catch (Exception e) {
            return 0;
        }
    }

    public static long getTotalSpace(String path, boolean bwait) {
        try {
            if (bwait)
                Thread.sleep(2000);
            StatFs sf = new StatFs(path);
            long blockSize = sf.getBlockSize();
            long count = sf.getBlockCount();
            return blockSize * count;
        } catch (Exception e) {
            return 0;
        }
    }


    private static synchronized List<SDevice> scanExternalDevices() {
        List<SDevice> list = new ArrayList<SDevice>();
        String sdPath = getSDCardPath(ROOT_FOLDER);
        String diskByte = "";
        if (CoocaaApplication.isCoocaaSystem()) {
            int majorVersion = -1;
            try {
                CoocaaApplication.CoocaaVersion version = CoocaaApplication.getCoocaaVersion();
                majorVersion = version.major;
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (majorVersion >= 4) {
                try {
                    diskByte = XServiceManager.getLiteCCConnectorService().getStorage();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

                if (diskByte != null && !diskByte.equals("")) {
                    LogUtils.i("", "sunny disk diskByte:" + diskByte);
                    String[] diskList = diskByte.split("@str:");
                    if (diskList != null) {
                        for (int i = 0; i < diskList.length; i++) {
                            if (diskList[i] != null && diskList[i].length() > 0) {
                                String str = "@str:" + diskList[i];
                                SkySystemUtil.ExternDiskInfo info = new SkySystemUtil.ExternDiskInfo(str);
                                SDevice device = new SDevice();
                                device.setLabel(info.diskInfo.label);
                                device.setMountPoint(info.diskInfo.path.toString());
                                device.setMounted(checkIsMounted(mContext, device.getMountPoint()));
                                device.setExternal(true);
                                getSDevice(device, sdPath);
                                LogUtils.i("", "sunny disk:" + diskList[i] + "; " + device.getLabel() + "; " + device.getMountPoint() + "; " + device.getMediaType());
                                if (device.isMounted())
                                    list.add(device);
                            }
                        }
                    }
                } else {
                    LogUtils.i("", "sunny disk: nodisk");
                }
            }
        }
        return list;
    }

    private static void getSDevice(SDevice device, String sdPath) {
        if (VERSION.SDK_INT >= 14) {
            if (device.getMountPoint().toLowerCase().contains("sdcard")
                    || (sdPath != null && device.getMountPoint().equals(sdPath))) {
                if (!(getTotalSpace(device, false) > 0))
                    return;
                device.setMediaType(MEDIA_TYPE.SDCARD);
            } else {
                device.setMediaType(MEDIA_TYPE.DISK);
            }
        } else {
            if (sdPath != null && device.getMountPoint().equals(sdPath)) {
                device.setMounted(true);
                device.setMediaType(MEDIA_TYPE.SDCARD);
            } else if (device.getMountPoint().startsWith("/mnt")) {
                File file = new File(device.getMountPoint() + File.separator + "temp");
                if (file.exists()) {
                    device.setMounted(true);
                } else {
                    if (file.mkdir()) {
                        file.delete();
                        device.setMounted(true);
                    } else {
                        device.setMounted(false);
                    }
                }
                device.setMediaType(MEDIA_TYPE.DISK);
            }
        }
    }

    private static boolean checkIsMounted(Context context, String mountPoint) {
        if (mountPoint == null) {
            return false;
        }
        StorageManager storageManager = (StorageManager) context
                .getSystemService(Context.STORAGE_SERVICE);
        try {
            Method getVolumeState = storageManager.getClass().getMethod("getVolumeState",
                    String.class);
            String state = (String) getVolumeState.invoke(storageManager, mountPoint);
            return Environment.MEDIA_MOUNTED.equals(state);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private static synchronized List<SDevice> scanSDevices() {
        List<SDevice> list = null;
        if (VERSION.SDK_INT >= 14)
            list = upper14.getSDevices(mContext);
        else
            list = below14.getSDevices(mContext);
        List<SDevice> extenalList = scanExternalDevices();
        for (SDevice device : list) {
            if (extenalList != null && extenalList.size() > 0) {
                if (containsDevice(device, extenalList)) {
                    device.setExternal(true);
                }
                LogUtils.i("", "sunny device external:" + device.getMountPoint() + ";" + device.isExternal());
            }
        }
        return list;
    }

    private static interface IGetSDevices {
        List<SDevice> getSDevices(Context context);
    }

    private static IGetSDevices below14 = new IGetSDevices() {
        @Override
        public List<SDevice> getSDevices(Context context) {
            // TODO Auto-generated method stub
            List<SDevice> devices = new ArrayList<SDevice>();
            String sdPath = getSDCardPath(ROOT_FOLDER);
            BufferedReader bufferedReader = null;
            List<String> dev_mountStrs = null;
            try {
                // API14以下通过读取Linux的vold.fstab文件来获取SDCard信息
                bufferedReader = new BufferedReader(
                        new FileReader(Environment.getRootDirectory().getAbsoluteFile()
                                + File.separator + "etc" + File.separator + "vold.fstab"));
                dev_mountStrs = new ArrayList<String>();
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    if (line.startsWith("dev_mount")) {
                        dev_mountStrs.add(line);
                    }
                }
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            for (int i = 0; dev_mountStrs != null && i < dev_mountStrs.size(); i++) {
                SDevice device = new SDevice();
                String[] infoStr = dev_mountStrs.get(i).split(" ");
                device.setLabel(infoStr[1]);
                device.setMountPoint(infoStr[2]);
                if (sdPath != null && device.getMountPoint().equals(sdPath)) {
                    device.setMounted(true);
                    device.setMediaType(MEDIA_TYPE.SDCARD);
                } else if (device.getMountPoint().startsWith("/mnt")) {
                    File file = new File(device.getMountPoint() + File.separator + "temp");
                    if (file.exists()) {
                        device.setMounted(true);
                    } else {
                        if (file.mkdir()) {
                            file.delete();
                            device.setMounted(true);
                        } else {
                            device.setMounted(false);
                        }
                    }
                }
                if (device.isMounted())
                    devices.add(device);
            }
            return devices;
        }
    };

    private static IGetSDevices upper14 = new IGetSDevices() {
        @Override
        public List<SDevice> getSDevices(Context context) {
            String sdPath = getSDCardPath(ROOT_FOLDER);
            List<SDevice> devices = new ArrayList<SDevice>();
            StorageVolume[] storageVolumes = null;
            try {
                StorageManager storageManager = (StorageManager) context
                        .getSystemService(Context.STORAGE_SERVICE);
                Method getVolumePaths = storageManager.getClass().getMethod("getVolumeList");
                storageVolumes = (StorageVolume[]) getVolumePaths.invoke(storageManager);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (storageVolumes != null && storageVolumes.length > 0) {
                LogUtils.d("usb", "" + storageVolumes.length);
                for (int i = 0; i < storageVolumes.length; i++) {
                    StorageVolume mStorageVolume = storageVolumes[i];
                    SDevice device = new SDevice();
                    device.setID(String.valueOf(mStorageVolume.getStorageId()));
                    device.setMountPoint(mStorageVolume.getPath());
                    device.setMounted(checkIsMounted(context, device.getMountPoint()));
                    device.setEmulated(mStorageVolume.isEmulated());
                    if (device.getMountPoint().toLowerCase().contains("sdcard")
                            || (sdPath != null && device.getMountPoint().equals(sdPath))) {
                        if (!(getTotalSpace(device, false) > 0))
                            continue;
                        device.setMediaType(MEDIA_TYPE.SDCARD);
                    } else
                        device.setMediaType(MEDIA_TYPE.DISK);
                    if (device.isMounted())
                        devices.add(device);
                }
            }
            LogUtils.d("usb", "devices list .size:" + devices.size());
            return devices;
        }

        private boolean checkIsMounted(Context context, String mountPoint) {
            if (mountPoint == null) {
                return false;
            }
            StorageManager storageManager = (StorageManager) context
                    .getSystemService(Context.STORAGE_SERVICE);
            try {
                Method getVolumeState = storageManager.getClass().getMethod("getVolumeState",
                        String.class);
                String state = (String) getVolumeState.invoke(storageManager, mountPoint);
                return Environment.MEDIA_MOUNTED.equals(state);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
    };

    private static void chmod(String path, int flag) {
        try {
            String cmd = "chmod ";
            if (new File(path).isDirectory())
                cmd += "-R " + flag + " " + path;
            else
                cmd += flag + " " + path;
            Runtime.getRuntime().exec(cmd);
            LogUtils.d("chmod cmd:" + cmd);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
